﻿using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using HPSocketCS;
using System.Runtime.InteropServices;
using System.Collections.Concurrent;

namespace Server
{
    public partial class Frm_Server : Form
    {
        /// <summary>
        /// 组件状态
        /// </summary>
        private AppState appState = AppState.Stoped;

        /// <summary>
        /// 显示消息的委托
        /// </summary>
        /// <param name="msg">消息内容</param>
        private delegate void ShowMsg(string msg);
        private ShowMsg AddMsgDelegate;

        /// <summary>
        /// tcp服务端组件
        /// </summary>
        TcpPackServer aTcpServerObj = new TcpPackServer();

        /// <summary>
        /// 附加数据列表
        /// </summary>
        Extra<ClientInfo> extra = new Extra<ClientInfo>();

        /// <summary>
        /// 连接的客户端
        /// </summary>
        ConcurrentDictionary<IntPtr, ClientStateInfo> aClientInfoList = new ConcurrentDictionary<IntPtr, ClientStateInfo>();

        /// <summary>
        /// 保存文件的路径
        /// </summary>
        private readonly string aFilePath = @"Files\";

        public Frm_Server()
        {
            InitializeComponent();
        }

        private void Frm_Main_Load(object sender, EventArgs e)
        {
            try
            { 
                // 加个委托显示msg,因为on系列都是在工作线程中调用的,ui不允许直接操作
                AddMsgDelegate = new ShowMsg(AddMsg);


                // 设置服务器事件
                aTcpServerObj.OnAccept += new TcpServerEvent.OnAcceptEventHandler(OnAccept);
                aTcpServerObj.OnSend += new TcpServerEvent.OnSendEventHandler(OnSend);
                // 两个数据到达事件的一种
                aTcpServerObj.OnPointerDataReceive += new TcpServerEvent.OnPointerDataReceiveEventHandler(OnPointerDataReceive);
                // 两个数据到达事件的一种
                //server.OnReceive += new TcpServerEvent.OnReceiveEventHandler(OnReceive);

                aTcpServerObj.OnClose += new TcpServerEvent.OnCloseEventHandler(OnClose);
                aTcpServerObj.OnShutdown += new TcpServerEvent.OnShutdownEventHandler(OnShutdown);

                SetAppState(AppState.Stoped);

                AddMsg(string.Format("HP-Socket Version: {0}", aTcpServerObj.Version));
            }
            catch (Exception ex)
            {
                SetAppState(AppState.Error);
                AddMsg(ex.Message);
            }
        }

        HandleResult OnAccept(IntPtr connId, IntPtr pClient)
        {
            // 获取客户端ip和端口
            string ip = string.Empty;
            ushort port = 0;
            if (aTcpServerObj.GetRemoteAddress(connId, ref ip, ref port))
            {
                AddMsg(string.Format(" > [{0},OnAccept] -> PASS({1}:{2})", connId, ip.ToString(), port));
            }
            else
            {
                AddMsg(string.Format(" > [{0},OnAccept] -> Server_GetClientAddress() Error", connId));
            }

            ClientStateInfo iObj = new ClientStateInfo();
            iObj.ConnID = connId;
            iObj.State = ClientState.Wait;
            iObj.IpAddress = ip;
            iObj.Port = port;
            aClientInfoList[connId] = iObj;

            // 设置附加数据
            ClientInfo clientInfo = new ClientInfo();
            clientInfo.ConnId = connId;
            clientInfo.IpAddress = ip;
            clientInfo.Port = port;
            if (extra.Set(connId, clientInfo) == false)
            {
                AddMsg(string.Format(" > [{0},OnAccept] -> SetConnectionExtra fail", connId));
            }

            return HandleResult.Ok;
        }

        HandleResult OnSend(IntPtr connId, byte[] bytes)
        {
            AddMsg(string.Format(" > [{0},OnSend] -> ({1} bytes)", connId, bytes.Length));

            return HandleResult.Ok;
        }

        HandleResult OnPointerDataReceive(IntPtr connId, IntPtr pData, int length)
        {
            // 数据到达了
            try
            {
                if (!aClientInfoList.ContainsKey(connId)) return HandleResult.Ok;

                ClientStateInfo iStateObj = aClientInfoList[connId];
                byte[] ibytes = new byte[length];
                Marshal.Copy(pData, ibytes, 0, length);
                byte iOneByte = ibytes[0];
                
                switch(iOneByte)
                {
                    case 1:
                        {
                            byte[] idatas = new byte[length - 1];
                            Array.Copy(ibytes, 1, idatas, 0, length - 1);
                            SendFileInfo iSendFileInfo = aTcpServerObj.BytesToStruct<SendFileInfo>(idatas);
                            AddMsg(string.Format(" > [{0},OnReceive] -> 收到准备发文件的指令，文件名：{1}；文件大小：:{2}字节"
                                , connId, iSendFileInfo.FileName, iSendFileInfo.FileSize));
                            string iFilePath = aFilePath + iSendFileInfo.FileName;
                            if(File.Exists(iFilePath))
                            {
                                string iFileName = Path.GetFileNameWithoutExtension(iFilePath);
                                string iFileExt = Path.GetExtension(iFilePath);
                                iSendFileInfo.FileName = iFileName + "_"
                                    + DateTime.Now.ToString("yyyyMMddHHmmss")
                                    + iFileExt;
                                iFilePath = aFilePath + iSendFileInfo.FileName;
                            }
                            FileStream iFileStreamObj = new FileStream(iFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                            iStateObj.CurrentFileStream = iFileStreamObj;
                            iStateObj.ReceiveLength = 0;
                            iStateObj.SendFileInfo = iSendFileInfo;
                            aTcpServerObj.Send(connId, ibytes, ibytes.Length);
                            iStateObj.State = ClientState.SendFile;
                            break;
                        }
                    case 2:
                        {
                            byte[] idatas = new byte[length - 1];
                            Array.Copy(ibytes, 1, idatas, 0, length - 1);
                            SendFileInfo iSendFileInfo = iStateObj.SendFileInfo;
                            string iFilePath = aFilePath + iSendFileInfo.FileName;
                            FileStream iFileStreamObj = iStateObj.CurrentFileStream;
                            iFileStreamObj.Write(idatas, 0, idatas.Length);
                            iStateObj.ReceiveLength += idatas.Length;

                            if (iStateObj.ReceiveLength == iSendFileInfo.FileSize)
                            {
                                AddMsg(string.Format(" > [{0},OnReceive] -> 接收文件完成，文件名：{1}；保存路径：{2}"
                                    , connId, iSendFileInfo.FileName, iFilePath));
                                iFileStreamObj.Flush();
                                iFileStreamObj.Close();
                                iFileStreamObj.Dispose();

                                string iFileFullPath = Path.GetFullPath(aFilePath + iSendFileInfo.FileName);
                                byte[] iFilePath_Bytes = Encoding.Unicode.GetBytes(iFileFullPath);
                                byte[] iSendDatas = new byte[iFilePath_Bytes.Length + 1];
                                iSendDatas[0] = 3;
                                Array.Copy(iFilePath_Bytes, 0, iSendDatas, 1, iFilePath_Bytes.Length);
                                aTcpServerObj.Send(connId, iSendDatas, iSendDatas.Length);

                                iStateObj.CurrentFileStream = null;
                                iStateObj.ReceiveLength = 0;
                                iStateObj.State = ClientState.Wait;
                            }
                            break;
                        }
                    case 4:
                        {
                            if (iStateObj.State == ClientState.SendFile) 
                                iStateObj.State = ClientState.Error;
                            if(iStateObj.CurrentFileStream!=null)
                            {
                                iStateObj.CurrentFileStream.Flush();
                                iStateObj.CurrentFileStream.Close();
                                iStateObj.CurrentFileStream.Dispose();
                            }
                            iStateObj.ReceiveLength = 0;
                            break;
                        }
                }
                return HandleResult.Ok;
            }
            catch (Exception Err)
            {
                AddMsg(string.Format(" > [{0},OnReceive] -> 发生错误：{1}", connId, Err.Message));
                return HandleResult.Ignore;
            }
        }

        HandleResult OnClose(IntPtr connId, SocketOperation enOperation, int errorCode)
        {
            if (!aClientInfoList.ContainsKey(connId)) return HandleResult.Ok;
            ClientStateInfo iStateObj = aClientInfoList[connId];
            if (iStateObj.State == ClientState.SendFile)
                iStateObj.State = ClientState.Wait;
            if (iStateObj.CurrentFileStream != null)
            {
                iStateObj.CurrentFileStream.Flush();
                iStateObj.CurrentFileStream.Close();
                iStateObj.CurrentFileStream.Dispose();
            }
            iStateObj.ReceiveLength = 0;
            aClientInfoList.TryRemove(connId, out iStateObj);

            if (errorCode == 0)
                AddMsg(string.Format(" > [{0},OnClose]", connId));
            else
                AddMsg(string.Format(" > [{0},OnError] -> OP:{1},CODE:{2}", connId, enOperation, errorCode));

            if (extra.Remove(connId) == false)
            {
                AddMsg(string.Format(" > [{0},OnClose] -> SetConnectionExtra({0}, null) fail", connId));
            }

            return HandleResult.Ok;
        }

        HandleResult OnShutdown()
        {
            foreach(ClientStateInfo iObj in aClientInfoList.Values)
            {
                if(iObj.CurrentFileStream!=null)
                {
                    iObj.CurrentFileStream.Flush();
                    iObj.CurrentFileStream.Close();
                    iObj.CurrentFileStream.Dispose();
                }
            }
            aClientInfoList.Clear();
            AddMsg(" > [OnShutdown]");
            return HandleResult.Ok;
        }

        /// <summary>
        /// 设置程序状态
        /// </summary>
        /// <param name="state"></param>
        void SetAppState(AppState state)
        {
            appState = state;
        }

        /// <summary>
        /// 往listbox加一条项目
        /// </summary>
        /// <param name="msg"></param>
        void AddMsg(string msg)
        {
            if (lis_Msg.InvokeRequired)
            {
                lis_Msg.Invoke(AddMsgDelegate, msg);
            }
            else
            {
                if (lis_Msg.Items.Count > 1000)
                {
                    using (StreamWriter iLogFileObj = new StreamWriter(@"log.txt", true, Encoding.Unicode))
                    {
                        foreach(object iobj in lis_Msg.Items)
                        {
                            iLogFileObj.WriteLine(iobj.ToString());
                        }
                    }

                    lis_Msg.Items.Clear();
                }
                lis_Msg.Items.Add(msg);
            }
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            try
            {
                if (appState != AppState.Started)
                {
                    String ip = txt_IP.Text.Trim();
                    ushort port = ushort.Parse(txt_Port.Text.Trim());

                    // 写在这个位置是上面可能会异常
                    SetAppState(AppState.Starting);
                    aTcpServerObj.IpAddress = ip;
                    aTcpServerObj.Port = port;
                    // 启动服务
                    if (aTcpServerObj.Start())
                    {
                        SetAppState(AppState.Started);
                        AddMsg(string.Format("$Server Start OK -> ({0}:{1})", ip, port));
                        btn_Start.Text = "结束监听";
                    }
                    else
                    {
                        SetAppState(AppState.Stoped);
                        throw new Exception(string.Format("$Server Start Error -> {0}({1})", aTcpServerObj.ErrorMessage, aTcpServerObj.ErrorCode));
                    }
                }
                else
                {
                    SetAppState(AppState.Stoping);

                    // 停止服务
                    AddMsg("$Server Stop");
                    if (aTcpServerObj.Stop())
                    {
                        SetAppState(AppState.Stoped);
                        btn_Start.Text = "开始监听";
                    }
                    else
                    {
                        AddMsg(string.Format("$Stop Error -> {0}({1})", aTcpServerObj.ErrorMessage, aTcpServerObj.ErrorCode));
                    }
                }
            }
            catch (Exception ex)
            {
                AddMsg(ex.Message);
            }
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public class ClientInfo
    {
        public IntPtr ConnId { get; set; }
        public string IpAddress { get; set; }
        public ushort Port { get; set; }
    }

    /// <summary>
    /// 组件状态
    /// </summary>
    public enum AppState
    {
        Starting, Started, Stoping, Stoped, Error
    }

    /// <summary>
    /// 文件信息
    /// </summary>
    public struct SendFileInfo
    {
        /// <summary>
        /// 文件名称
        /// </summary>
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
        public string FileName;

        /// <summary>
        /// 文件大小
        /// </summary>
        public long FileSize;
    }

    public class ClientStateInfo
    {
        /// <summary>
        /// 标识
        /// </summary>
        public IntPtr ConnID { get; set; }

        /// <summary>
        /// 当前动作
        /// </summary>
        public ClientState State { get; set; }

        /// <summary>
        /// 如果当前动作是在发文件，则保存正在发送的文件信息
        /// </summary>
        public SendFileInfo SendFileInfo { get; set; }

        /// <summary>
        /// 如果当前动作是在发文件，则保存正在发送的文件句柄
        /// </summary>
        public FileStream CurrentFileStream { get; set; }

        /// <summary>
        /// 如果当前动作是在发文件，则保存已经接收的数据长度
        /// </summary>
        public long ReceiveLength { get; set; }

        /// <summary>
        /// IP地址
        /// </summary>
        public string IpAddress { get; set; }

        /// <summary>
        /// 端口
        /// </summary>
        public ushort Port { get; set; }

        /// <summary>
        /// 最后接收的时间
        /// </summary>
        public DateTime LastTime { get; set; }
    }

    public enum ClientState
    {
        /// <summary>
        /// 发生错误
        /// </summary>
        Error,
        /// <summary>
        /// 等待数据
        /// </summary>
        Wait,
        /// <summary>
        /// 发文件
        /// </summary>
        SendFile
    }
}
